package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.canal.goods.feign.SkuFeign;
import com.changgou.canal.goods.pojo.Sku;
import com.changgou.search.dao.SkuEsMapper;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SkuService;
import entity.Result;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * @author 秦川熙
 */
@Service
public class SkuServiceImpl implements SkuService {

    @Autowired
    private SkuFeign skuFeign;

    @Autowired
    private SkuEsMapper skuEsMapper;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    /**
     * 导入sku数据到es
     */
    @Override
    public void importEs() {
        //调用changgou-service-goods微服务
        Result<List<Sku>> skuListResult = skuFeign.findByStatus("1");
        //将数据转成search.Sku
        List<SkuInfo> skuInfos = JSON.parseArray(JSON.toJSONString(skuListResult.getData()),SkuInfo.class);
        for(SkuInfo skuInfo:skuInfos){
            Map<String, Object> specMap= JSON.parseObject(skuInfo.getSpec()) ;
            skuInfo.setSpecMap(specMap);
        }
        skuEsMapper.saveAll(skuInfos);
    }

    /**
     * 搜索
     * @param searchMap
     * key:
     *      category     商品分类的过滤查询
     *
     *      brand        商品品牌的过滤查询   TCL
     *
     *      spec_规格的名称   value  规格的值      规格相关
     *
     *      price           value 价格区间的字符串 0-500   3000-*
     *
     *
     *      pageNum   当前的页码
     *      pageSize  不用传递   (写死)
     *
     *
     *      sortField  要排序的字段  price
     *      sortRule   要排序的规则 (ASC DESC)
     * @return
     */
    @Override
    public Map search(Map<String, String> searchMap) {

        //1、获取关键字的值
        String keywords = searchMap.get("keywords");

        if (StringUtils.isEmpty(keywords)) {
            //赋值给一个默认的值
            keywords = "华为";
        }

        //2、创建查询对象的构建对象
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

        /**
         * 设置分组条件 商品分类
         * 分组查询分类集合
         * .addAggregation:添加一个聚合操作
         * AggregationBuilders.terms("skuCategorygroup")：取别名
         * field("categoryName")：表示根据哪个域进行分组查询
         */
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuCategorygroup").field("categoryName").size(50));

        //设置分组条件 商品品牌
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuBrandgroup").field("brandName").size(100));

        //设置分组条件 商品规格
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpecgroup").field("spec.keyword").size(500));

        //设置高亮的字段 设置前缀和后缀
        //设置高亮的字段 针对商品的名称进行高亮
        nativeSearchQueryBuilder.withHighlightFields(new HighlightBuilder.Field("name"));
        //设置前缀和后缀
        nativeSearchQueryBuilder.withHighlightBuilder(new HighlightBuilder().preTags("<em style=\"color:red\">").postTags("</em>"));

        //匹配查询  先分词 在查询 主条件查询
        //设置主关键字查询
        //nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("name", keywords));
        nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery(keywords, "name", "category", "brandName"));

        //添加多添加组合查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //过滤查询的条件设置  商品分类的条件
        String category = searchMap.get("category");

        if (!StringUtils.isEmpty(searchMap.get(category))) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("categoryName", category));
        }

        //过滤查询的条件设置  商品品牌的条件
        String brand = searchMap.get("brand");

        if (!StringUtils.isEmpty(searchMap.get(brand))) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brandName", brand));
        }

        //过滤查询的条件设置  规格条件
        if (searchMap != null) {
            //{brand:"",category:"",spec_网络:"电信4G"}
            for (String key : searchMap.keySet()) {
                //截取规格的名称
                boolQueryBuilder.filter(QueryBuilders.termQuery("specMap." + key.substring(5) + ".keyword", searchMap.get(key)));
            }
        }

        //过滤查询的条件设置  价格区间的过滤查询
        String price = searchMap.get("price");

        if (!StringUtils.isEmpty(price)) {
            //获取值  按照“-”切割
            String[] split = price.split("-");
            //过滤范围查询  0 <= price <= 500
            if (!split[1].equals("*")) {
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").from(split[0], true).to(split[1], true));
            } else {
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(split[0]));
            }
        }

        //构建过滤查询
        nativeSearchQueryBuilder.withFilter(boolQueryBuilder);

        /**
         * 分页查询
         * 第一个参数：指定当前的页码。 注意：如果是第一页，数值为0
         * 第二个参数：指定当前的页的显示的行
         */
        String pageNum1 = searchMap.get("pageNum");
        Integer pageNum = Integer.valueOf(pageNum1);

        Integer pageSize = 30;

        nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNum - 1, pageSize));

        //排序操作
        //获取排序的字段 和要排序的规则
        String sortField = searchMap.get("sortField");
        String sortRule = searchMap.get("sortRule");
        if (!StringUtils.isEmpty(sortField) && !StringUtils.isEmpty(sortRule)) {
            //执行排序
            nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField).order(sortRule.equalsIgnoreCase("ASC") ? SortOrder.ASC : SortOrder.DESC));
        }

        //4、构建查询对象
        NativeSearchQuery query = nativeSearchQueryBuilder.build();

        //5、执行查询
        AggregatedPage<SkuInfo> skuPage = elasticsearchTemplate.queryForPage(query, SkuInfo.class);


        /**
         * 获取分组结果 商品分类
         * (StringTerms) skuPage.getAggregation();获取的是集合，可以根据多个域进行分组
         * skuPage.getAggregation("skuCategorygroup");获取指定域的集合数【手机、家用电器、手机配件】
         */
        StringTerms stringTermsCategory = (StringTerms) skuPage.getAggregation("skuCategorygroup");

        //获取分组结果， 商品品牌
        StringTerms stringTermsBrand = (StringTerms) skuPage.getAggregation("skuBrandgroup");

        //获取分组结果， 商品规格
        StringTerms stringTermsSpec = (StringTerms) skuPage.getAggregation("skuSpecgroup");

        //获取分类列表数据
        List<String> categoryList = getStringsCategoryList(stringTermsCategory);

        //获取品牌列表
        List<String> brandList = getStringsBrandList(stringTermsBrand);

        //获取规格列表
        Map<String, Set<String>> specMap = getStringsSpecMap(stringTermsSpec);

        //6、返回结果
        Map resultMap = new HashMap<>();
        resultMap.put("rows", skuPage.getContent());
        resultMap.put("brandList", brandList);
        resultMap.put("specMap", specMap);
        resultMap.put("total", skuPage.getTotalElements());
        resultMap.put("totalPages", skuPage.getTotalPages());
        resultMap.put("categoryList", categoryList);
        resultMap.put("pageNum", pageNum);
        resultMap.put("pageSize", pageSize);
        return resultMap;
    }

    /**
     * 获取分类列表数据
     * @param stringTerms
     * @return
     */
    private List<String> getStringsCategoryList(StringTerms stringTerms) {
        List<String> categoryList = new ArrayList<>();
        if (stringTerms != null) {
            for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
                //分组的值
                String keyAsString = bucket.getKeyAsString();
                categoryList.add(keyAsString);
            }
        }
        return categoryList;
    }

    /**
     * 获取品牌列表
     * @param stringTermsBrand
     * @return
     */
    private List<String> getStringsBrandList(StringTerms stringTermsBrand) {
        List<String> brandList = new ArrayList<>();
        if (stringTermsBrand != null) {
            for (StringTerms.Bucket bucket : stringTermsBrand.getBuckets()) {
                brandList.add(bucket.getKeyAsString());
            }
        }
        return brandList;
    }

    /**
     * 获取规格列表数据
     * @param stringTermsSpec
     * @return
     */
    private Map<String, Set<String>> getStringsSpecMap(StringTerms stringTermsSpec) {
        Map<String, Set<String>> specMap = new HashMap<>();
        Set<String> specList = new HashSet<>();
        if (stringTermsSpec != null) {
            for (StringTerms.Bucket bucket : stringTermsSpec.getBuckets()) {
                specList.add(bucket.getKeyAsString());
            }
        }

        for (String specjson : specList) {
            Map<String, String> map = JSON.parseObject(specjson, Map.class);
            for (Map.Entry<String, String> entry : map.entrySet()) {
                //规格名字
                String key = entry.getKey();
                //规格选项值
                String value = entry.getValue();
                //获取当前规格名字对应的规格数据
                Set<String> specValues = specMap.get(key);
                if (specValues == null) {
                    specValues = new HashSet<String>();
                }
                //将当前规格加入到集合中
                specValues.add(value);
                //将数据存入到specMap中
                specMap.put(key, specValues);
            }
        }
        return specMap;
    }

}
